from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal,QVariant,QAbstractItemModel,QModelIndex, QByteArray, Qt,QObject, pyqtProperty, pyqtSignal as Signal,pyqtSlot as Slot, QPointF,QAbstractTableModel
from PyQt5.QtGui import QColor,QGuiApplication,QClipboard
from PyQt5 import QtCore
from PyQt5.QtQuick import (QQuickItem, QSGFlatColorMaterial,
                             QSGGeometry, QSGGeometryNode, QSGNode)
from PyQt5.QtQml import QQmlComponent #QmlElement
import json
import logging
# import pandas
import myglobal
roleDataType = Qt.UserRole
roleView=Qt.UserRole+1
roleCommand=Qt.UserRole+2
roleHighlight=Qt.UserRole+3
roleVisible=Qt.UserRole+4
roleRef1=Qt.UserRole+5
Sqlite = 0
JsonISO=1
class MySet:
    def __init__(self):
        self._data=[]
    def count(self,v):
        if v in self._data:
            return 1
        return 0
    def erase(self,v):
        try:
            self._data.remove(v)
        except ValueError:
            pass
        pass
    def insert(self,v):
        logging.info([v,self._data])
        if v in self._data:
            logging.info("already in")
            pass
        else:
            self._data.append(v)
            self._data.sort()
            logging.info(self._data)
    def size(self):
        return len(self._data)
    def empty(self):
        if len(self._data)==0:
            return True
        else:
            return False
    def begin(self):
        return self._data[0]
    def end(self):
        return self._data[len(self._data)-1]
    def rbegin(self):
        return self.end()
    def rend(self):
        return self.begin()
    def clear(self):
        self._data=[]
    pass
class BTModel(QAbstractTableModel):
    allCheckedChanged=pyqtSignal()
    def __init__(self,parent=None):
        super().__init__(parent)
        self.dbRef=None
        self.dataChangedALS=False
        self.columnsChangedALS=False
        self.sortedChangedALS=False
        self._highlightRow=-1
        self.lastHighlightRow=-1
        self._hasMultiselection=True
        self._multiselectionMobileMode=False
        self.onlyHighlightRole=roleHighlight #onlyHighlightRole{CustomRoles.roleHighlight}
        self.sortColumns=[] #column ordinal, starting from 1 (not 0!)
        self._highlightRow=-1
        self._data=[]
        self.dataIndex=[]
        self.properties=[]
        self.highlightRows= MySet() #set()
        # self.properties=[{"dataRef1":None},{"dataRef1":None},{"dataRef1":None}]
        # self.demo()
    headerRolesChanged=Signal()
    @pyqtProperty(list, notify=headerRolesChanged)
    def headerRoles(self):
        logging.info(self.properties)
        r= [p.get("role") for  p in self.properties]
        logging.info(r)
        return r
    @pyqtSlot(int,bool)
    def check(self, row, checked):
        self._data[row]["isChecked"]=checked
    @pyqtSlot()
    def doMove(self):
        pass
    @pyqtSlot()
    def doRelease(self):
        pass  
    @pyqtSlot()
    def doPress(self):
        pass  
    @pyqtSlot()
    def setAllChecked(self):
        self.beginResetModel()
        for one in self._data:
            one["isChecked"]=True
        self.endResetModel()
        pass
    @pyqtSlot()
    def initData(self):
        pass
    @pyqtSlot()
    def addOne(self):
        s={"a0":1,"a1":2,"a2":3,"isSelected":False,"isChecked":False,"isAlternate":False}
        self.append(s)
    @pyqtSlot()
    def addMulti(self):
        pass
    @pyqtSlot()
    def insertBeforeSelected(self):
        pass
    @pyqtSlot()
    def clearAll(self):
        pass
    @pyqtSlot()
    def removeSelected(self):
        pass
    @pyqtSlot()
    def removeChecked(self):
        pass
    @pyqtSlot()
    def selectAll(self):
        pass
    @pyqtSlot()
    def deselectAll(self):
        pass
    def gen_choise(self):
        i = 0
        r = []
        # logging.info(self.properties)
        for p in self.properties:
            if p["delegate"] is None:
                r.append("""DelegateChoice {
                    column: %d
                    delegate: TextField  {
                        text: model.display
                        implicitWidth: %d
                        readOnly:true
                    }
                }""" % (i, p["width"]))
            elif "edit" == p["delegate"]:
                r.append("""DelegateChoice {
                    column: %d
                    delegate: TextField  {
                        text: model.display
                        implicitWidth: %d
                        onEditingFinished: ()=>{
                            console.log(text)
                            model.display = text
                        } 
                    }
                }""" % (i, p["width"]))
            elif "check" == p["delegate"]:
                r.append("""DelegateChoice {
                    column: %d
                    delegate: CheckBox {
                        checked: model.display
                        onToggled: model.display = checked
                    }
                }""" % i)
            elif "double" == p["delegate"]:
                r.append("""DelegateChoice {
                    column: %d
                    delegate: TextField {
                        property var value: model.display
                        implicitWidth: %d
                        onValueChanged: ()=>{
                            //console.log(value)
                            let r=parseFloat(value)
                            if (isNaN(r)){
                                text=value
                            }else{
                                text=r.toFixed(%d)
                            }
                        }
                        readOnly:true
                    }
                }""" % (i, p["width"], p["point_num"]))
            elif "floatEdit" == p["delegate"]:
                r.append("""DelegateChoice {
                    column: %d
                    delegate: TextField  {
                        text: model.display
                        implicitWidth: %d
                        onEditingFinished: ()=>{
                            console.log(text)
                            model.display = text
                        } 
                    }
                }""" % (i, p["width"]))
            i += 1
        return "".join(r)

    @Slot(result=QObject)
    def createChooser(self):
        choies = self.gen_choise()
        qml = """import QtQuick 2.15
            import QtQuick.Controls 2.15
            import Qt.labs.qmlmodels 1.0
            DelegateChooser {
                        %s
        }""" % choies
        component = QQmlComponent(myglobal.engine)
        logging.info(qml)
        component.setData(qml.encode("utf-8"), QUrl())
        chooser = component.create()
        logging.info(chooser)
        return chooser    


    propsChanged=Signal()
    @pyqtProperty(list, notify=propsChanged)
    def props(self):
        return self.properties

    def setObjects(self,data,properties=[]):
        self.beginResetModel()
        self._data=data
        self.properties=properties
        self.endResetModel()        
    def setObjectsOnly(self,data):
        self.beginResetModel()
        self._data=data
        self.endResetModel()  
    def demo(self):
        self.beginResetModel()
        self._data=[]
        for i in range(3):
            s={"a0":1,"a1":2,"a2":3,"isSelected":False,"isChecked":True,"isAlternate":False}
            self._data.append(s)
        c0={"role":"a0","type":"Int","width":100}
        c1={"role":"a1","type":"Int","width":100}
        c2={"role":"a2","type":"Int","width":100}
        self.properties=[c0,c1,c2]
        self.endResetModel()
        # self.endUpdateColumns()
        self.headerRolesChanged.emit()
    highlightRowChanged=Signal()
    @pyqtProperty(int, final=True)
    def highlightRow(self):
        return self._highlightRow
    # @highlightRow.setter
    # def highlightRow(self, value):
    #     self._highlightRow=value
    #     # self.setHighlightRow(value,None)

    multiselectionMobileModeChanged=Signal()
    @pyqtProperty(bool, final=True)
    def multiselectionMobileMode(self):
        return self._multiselectionMobileMode
    @multiselectionMobileMode.setter
    def multiselectionMobileMode(self, value):
        self.setMultiselectionMobileMode(value)

    hasMultiselectionChanged=Signal()
    @pyqtProperty(bool, final=True,notify=hasMultiselectionChanged)
    def hasMultiselection(self):
        return self._hasMultiselection
    @multiselectionMobileMode.setter
    def hasMultiselection(self, value):
        self.setHasMultiselection(value)
    @Slot(result=int)
    def rowCount(self,*args, **kwargs):
        r=len(self._data)
        # logging.info(r)
        return r
    @Slot(result=int)
    def columnCount(self,*args, **kwargs):
        r=len(self.properties) #.size();
        # logging.info(r)
        return r

    # @Slot(result=int)
    # def rowCount(self):
    #     return len(self._data)#.size()
    # @Slot(result=int)
    # def columnCount(self):
    #     return len(self.properties) #.size();

    def data(self,cellIndex, role):
        # logging.info(self._data)
        # logging.info(["here",cellIndex.row(),cellIndex.column()])
        cdef = self.getColumnDef(cellIndex.column() )

        if role==Qt.DisplayRole:
            return self.getDataDisplayRole(cellIndex)
        elif role==roleDataType:
            return cdef.get("type")
        elif role==roleView:
            return cdef.get("view")
        elif role== roleCommand:
            return cdef.get("command")
        elif role==roleHighlight:
            if( self.highlightRows.count( cellIndex.row() ) ):
                return True
            return False;
        elif role== roleVisible:
            return cdef.get("visible")
        elif role== roleRef1:
            reference = cdef.get("reference1")
            if(reference is None):
                return ""
            else:
                return self.getDataDisplayRole(cellIndex)# QModelIndex(cellIndex.row(), reference) ) 

        return None

    #https:#doc.qt.io/qt-5/qt.html#ItemDataRole-enum
    def roleNames(self):
        names = {}
        names[hash(Qt.DisplayRole)] = "display".encode()
        names[hash(roleDataType)] = "dataType".encode()
        names[hash(roleView)] = "view".encode()
        names[hash(roleCommand)] = "command".encode()
        names[hash(roleHighlight)] = "highlight".encode()
        names[hash(roleVisible)] = "visible".encode()
        names[hash(roleRef1)] = "ref1".encode()
        return names
    def sortData(self):
        return
        if(self.dataChangedALS or self.columnsChangedALS or self.sortedChangedALS):
            self.setHighlightRow(-1, 0);

            doSort=True
            if(_data.isEmpty()): 
                doSort = False;

            if(doSort):
                if(sortColumns.isEmpty()): # no sort
                    doSort = false;
                    if(not dataIndex.isEmpty()) :
                        beginResetModel();
                        dataIndex.clear();
                        endResetModel();

            if(doSort):
                dataIndex.fill(0, _data.size());
                # std.iota(dataIndex.begin(), dataIndex.end(), 0);

                sortIndexes=[]
                for sortCol in  sortColumns:
                    orderAsc=True
                    iCol = sortCol;
                    if(iCol < 0):
                        orderAsc = False;
                        iCol = -iCol;

                    #-1 because sortColumns value starts from 1 for the first column
                    sortIndexes.push_back( QPair<int,bool>(iCol - 1, orderAsc) );

                self.beginResetModel();
                self.sort(dataIndex.begin(), dataIndex.end(), self.sortFunc)
                self.endResetModel();
            dataChangedALS = false;
            columnsChangedALS = false;
            sortedChangedALS = false;

    def sortFunc(self,indexA,  indexB):

        # recordA = self._data[indexA];
        # recordB = self._data[indexB];
        # for sortCol in  sortIndexes:
        #     orderAsc=sortCol.second
        #     iCol=sortCol.first

        #     valA = recordA[iCol];
        #     valB = recordB[iCol];

        #     if(valA.isNull() && !valB.isNull())
        #         return orderAsc;
        #     if(valB.isNull() && !valA.isNull())
        #         return !orderAsc;

        #     switch (getColumnDef( iCol ).type) {
        #     case "Str":
        #         if(valA.toString().compare(valB.toString(), Qt.CaseInsensitive) == 0):
        #             continue;
        #         break;
        #     case "Dbl":
        #         if( fabs(valA.toDouble() - valB.toDouble()) < double(FLT_EPSILON) )
        #             continue;
        #         break;
        #     case "Int":
        #         if( valA.toInt() == valB.toInt() )
        #             continue;
        #         break;
        #     case "Date":
        #         if( valA.toDate() == valB.toDate() )
        #             continue;
        #         break;
        #     case "DateTime":
        #         if( valA.toDateTime() == valB.toDateTime() )
        #             continue;
        #         break;

        #     if(orderAsc):
        #         switch (getColumnDef( iCol ).type) {
        #         case "Str":
        #             return valA.toString().compare(valB.toString(), Qt.CaseInsensitive) < 0;
        #         case "Dbl":
        #             return valA.toDouble() < valB.toDouble();
        #         case "Int":
        #             return valA.toInt() < valB.toInt();
        #         case "Date":
        #             return valA.toDate() < valB.toDate();
        #         case "DateTime":
        #             return valA.toDateTime() < valB.toDateTime();
        #     else:
        #         switch (getColumnDef( iCol ).type) {
        #         case "Str":
        #             return valA.toString().compare(valB.toString(), Qt.CaseInsensitive) > 0;
        #         case "Dbl":
        #             return valA.toDouble() > valB.toDouble();
        #         case "Int":
        #             return valA.toInt() > valB.toInt();
        #         case "Date":
        #             return valA.toDate() > valB.toDate();
        #         case "DateTime":
        #             return valA.toDateTime() > valB.toDateTime();
        return False;
    @Slot(bool)
    def beginReset(self, appendMode):
        self.beginResetModel();
        # if(not appendMode):
        #     self._data=pandas.DataFrame() #.clear();
            #dataIndex.clear();
        self.dataChangedALS = True;

    @Slot()
    def endReset(self):
        self.endResetModel()
        self.sortData()

    def addFromQuery(self,theSqlQuery, parameters):
        # if(!dbRef->getDb()) {
        #     qDebug() << "No database";
        #     return false;
        # }

        # QSqlQuery query(*dbRef->getDb());
        # query.setForwardOnly(true);

        # if(!query.prepare(theSqlQuery)){
        #     qDebug() << "Error querying the db" << query.lastError().text();
        #     return false;
        # }

        # if(!parameters.isEmpty()) {
        #     for(auto& curParam: parameters) {
        #         query.addBindValue(curParam);
        #     }
        # }

        # bool allOk(true);
        # if(query.exec()) {

        #     beginReset(false);

        #     if(!addFrontRecords.isEmpty())
        #         allOk = addFromList(addFrontRecords, false);

        #     _data.reserve(100);
        #     int nCols = properties.size();
        #     while(query.next() && allOk) {
        #         _data.push_back(QVector<QVariant>());
        #         QVector<QVariant>& curRow = _data.last();
        #         curRow.reserve( nCols );

        #         for(int iCol = 0; iCol < nCols; iCol++) {
        #             #if(getColumnDef( iCol ).reference1 < 0)
        #                 appendDataVariant(curRow, query.value(iCol), getColumnDef( iCol ).type, DataDialect.Sqlite);
        #             #else
        #             #    appendDataVariant(curRow, emptyVInt, getColumnDef( iCol ).type, DataDialect.JsonISO);
        #         }
        #     }
        #     _data.shrink_to_fit();

        #     endReset();
        # }

        # if(query.lastError().isValid())
        # {
        #     allOk = false;
        #     qDebug() << "Error querying the db" << query.lastError().text();
        # }
        # query.finish();

        # return allOk;
        pass
    @Slot(str,bool)
    def addFromList(self,values, resetList):
        allOk=True
        logging.info([values,resetList])
        values=json.loads(values)
        self.beginResetModel();
        if(resetList):
            # self._data=pandas.DataFrame()#.clear();
            pass
            #dataIndex.clear();
        self.dataChangedALS = True;
        for curRecord in  values:
            logging.info(curRecord)
            curRow=[]
            for  iCol in range(len(self.properties)):#.size(); iCol++) {
                role=self.getColumnDef( iCol ).get("role")
                logging.info(["role",role])
                if(not(role in curRecord.keys()) ):
                    self.appendDataVariant(curRow, None, self.getColumnDef(iCol).get("type"), JsonISO);
                else:
                    self.appendDataVariant(curRow, curRecord[role], self.getColumnDef( iCol ).get("type"), JsonISO);
            if len(self._data.columns)==0:
                self._data[self.rowCount()]=pandas.Series(curRow)
                self._data=self._data.T
            else:
                self._data.loc[self.rowCount()]=pandas.Series(curRow)
        logging.info(self._data)
        self.endResetModel()
        self.sortData()
        return allOk;

    def setFrontRecords(self,values):
        self.addFrontRecords = values;

    def countFromQuery(self,theSqlQuery, parameters):
        if(not dbRef.getDb()):
            print("No database")
            return False;

        query=QSqlQuery(dbRef.getDb());
        query.setForwardOnly(True);

        if(not query.prepare(theSqlQuery)):
            logging.info( ["Error querying the db" , query.lastError().text()])
            return 0;

        if(not parameters.isEmpty()):
            for curParam in  parameters:
                query.addBindValue(curParam)

        numRecords=0

        allOk=True
        if(query.exec()):
            if(query.next()):
                numRecords = int(query.value(0))
        if(query.lastError().isValid()):
            allOk = False;
            logging.info(["Error querying the db" , query.lastError().text()])
        query.finish();

        if(allOk):
            return numRecords;

        return 0;
    @Slot(str)
    def addRecord(self, theData):
        # logging.info([theData,type(theData)])
        theData=json.loads(theData)
        logging.info(theData)
        self.dataChangedALS = True
        if len(self._data.columns)==0:
            logging.info("is None")
            curRow=pandas.Series()
            i=0
            for p in self.properties:
                curRow[p.get("role")]=theData[i]
                i+=1
            self._data[theData[0]]=curRow  #empty Frame
            self._data=self._data.T
        else:
            logging.info("not None")
            curRow=pandas.Series()
            i=0
            for p in self.properties:
                curRow[p.get("role")]=theData[i]
                i+=1
            self._data.loc[theData[0]]=curRow#only set data
        logging.info(self._data)
    @Slot()
    def updateLayout(self):
        self.layoutChanged.emit();
        self.sortData();

    def setDbRef(self,value):
        self.dbRef = value;
    @Slot()
    def endUpdateColumns(self):
        # columns=[p.get("attr") for p in self.properties]
        # self._data=pandas.DataFrame(columns=columns)
        # logging.info(self._data)
        self.calcReferenceColumns();
        self.calcSortColumns();
        self.updateLayout();
    @Slot(result=bool)
    def canHideColumns(self):
        return True;
    @Slot(int)
    def copyRowToClipboard(self, row):
        text = self.getRowString(row)
        clipboard = QGuiApplication.clipboard();

        clipboard.setText(text, QClipboard.Clipboard);
        if (clipboard.supportsSelection()):
            clipboard.setText(text, QClipboard.Selection);

        return True;

    def getRowString(self,row):
        toRet=""

        toRet+="{"

        iCol=0
        appended=0;
        for col in self.properties:
            if(col.command == 0):
                if(appended):
                    toRet+= ", " ;
                if(col.title is None):
                    toRet+= "column_"+str(iCol + 1)+":"
                else:
                    toRet+= col.title+":"
                if(col.type == "Str" or col.type == "Date" or col.type == "DateTime"):
                    toRet+= "\"" 
                    toRet+= str(self.data( self.index(row, iCol), Qt.DisplayRole))
                    toRet+= "\"" 
                else:
                    toRet+= str(self.data( self.index(row, iCol), Qt.DisplayRole))
                appended+=1;
            iCol+=1;
        toRet+="}"

        return toRet;

    def getRowObject(self, row):
        logging.info("here")
        toRet={}
        iCol = 0;
        for  col in self.properties:
            toRet[col.role] = self.getDataDisplayRole( self.index(row, iCol) )
            iCol+=1
        return toRet;
    @Slot(int,int,QVariant)
    def setCellValue(self, rowNum,  columnNum, data):
        logging.info([rowNum,columnNum,data])
        if(rowNum < 0) or (rowNum >= self.rowCount):
            return False;
        if(columnNum < 0 or columnNum >= len(self.properties)):
            return False;

        whatRow = rowNum;
        # if(len(self.dataIndex)!=0):
        #     whatRow = self.dataIndex[whatRow];

        if( columnNum + 1 in self.sortColumns or -(columnNum + 1) in self.sortColumns):
            #data need to be resorted
            self.beginReset(True);
            self._data.iloc[whatRow][columnNum] = data;
            self.endReset();
        else:
            self._data.iloc[whatRow][columnNum] = data;
            self.dataChanged.emit( self.index(rowNum, columnNum) , self.index(rowNum, columnNum) , [Qt.DisplayRole]);
        return True;
    @Slot(int,result=int)
    def getColWidth(self, columnId):
        theW=0
        # if(columnId<0):
        #     theW = 100;
        if self.properties[columnId].get('visible'):
            theW = int(self.properties[columnId].get("width"))
        return theW;
    @Slot()
    def clearColumnsDef(self):
        # logging.info("here")
        # for  col in self.properties:
        #     delete col;
        # }
        self.properties=[]
        # self._data=pandas.DataFrame()
        self.columnsChangedALS = True;
    @Slot(result=int)
    def addColumnDef(self):
        # logging.info("here")
        newCol=len(self.properties)
        self.properties.append( {"attr":newCol})
        self.columnsChangedALS = True;
        return newCol;
    @Slot(int,bool,str)
    def setColumnDef(self, columnId,  withDefaults, colDef):
        # logging.info([columnId,colDef,type(colDef)])
        colDef=json.loads(colDef)
        # logging.info([type(colDef.get("width"))])
        # logging.info("here")
        # self.modify(columnId,colDef, withDefaults)
        self.properties[columnId]=colDef
    # def modify(self,columnId,colDef, withDefaults):
    #     old=self.properties[columnId]
    #     if(colDef.get("width") is None):
    #         if(withDefaults): 
    #             self.width = 100
    #     else:
    #         self.width = int(colDef["width"])
        
    #     if(self.width == 0):
    #         self.width = 100;

    #     if(colDef.get("minWidth") is None):
    #         if(withDefaults):
    #             self.minWidth = 0;
    #     else:
    #         self.minWidth = int(colDef["minWidth"])

    #     if(colDef.get("title") is None):
    #         if(withDefaults):
    #             self.title = ""
    #     else:
    #         self.title = str(colDef["title"])

    #     if(colDef.get("sort") is None):
    #         if(withDefaults): 
    #             self.sortFlag = 0
    #     else:
    #         self.sortFlag = int(colDef["sort"])

    #     if(colDef.get("dataType") is None):
    #         if(withDefaults):
    #             self.type = "string"
    #     else:
    #         self.type = colDef.get("dataType")

    #     if(colDef.get("view") is None):
    #         if(withDefaults): 
    #             self.view = 0
    #     else:
    #         self.view = int(colDef["view"])
    #     self.command=colDef.get("command",0)
    #     self.role=colDef.get("role","")
    #     self.visible=colDef.get("visible",True)
    #     self.dataRef1=colDef.get("dataRef1","")
    def getColumnDef(self, columnId):
        # logging.info("here")
        if columnId<len(self.properties) and columnId>-1:
            return self.properties[columnId];
        return None
    @Slot(str,result=int)
    def getColumnId(self,columnRole):
        # logging.info("here")
        columnNum = -1;
        for  iCol in range(len(self.properties)):
            if (columnRole == self.properties[iCol].role):
                columnNum = iCol;
                break;
        return columnNum;

    def appendDataVariant(self,record, theValue, columnType, dia):
        # logging.info("here")
        if(theValue is None):
            record.append(None)
        else:
            record.append(theValue)

    def calcSortColumns(self):
        # logging.info("here")
        newSortColumns=[]
        for i in range(len(self.properties)):#int i = 0; i < properties.size(); i++){
            sortVal = self.getColumnDef(i).get("sortFlag")
            if(sortVal == 1):
                newSortColumns.append(i+1);
            if(sortVal == 2):
                newSortColumns.append(-(i+1));
        if(newSortColumns != self.sortColumns):
            self.sortColumns = newSortColumns;
            sortedChangedALS = True;

    def calcReferenceColumns(self):
        # logging.info("here")
        for iColumn in range(len(self.properties)):
            # logging.info([self.properties, iColumn])
            currentColumn = self.properties[iColumn];
            # logging.info([currentColumn,type(currentColumn)])
            if currentColumn.get("dataRef1") is None:
                currentColumn["reference1"] = -1;
            else:
                currentColumn["reference1"] = -1;
                iSeek=0
                # for(int iSeek = 0; ; iSeek++):
                while iSeek < len(self.properties) and currentColumn.get("reference1") == -1:
                    seekColumn = self.properties[iSeek]
                    if not seekColumn.get("role") is None and currentColumn.get("dataRef1")==seekColumn.get("role"):
                        currentColumn["reference1"] = iSeek;
                    iSeek+=1

    def getDataDisplayRole(self,index):
        # logging.info(["here",index.row(),index.column()])
        whatRow = index.row();
        # logging.info([whatRow,type(whatRow)])
        col=index.column()
        # logging.info([col,type(col)])
        colVal = self._data[whatRow]
        return colVal
        # r={"0":str(colVal["a0"]),"1":str(colVal["a1"]),"2":str(colVal["a2"]),"isSelected":False,"isChecked":False,"isAlternate":False}
        # logging.info(r)

        type1=self.getColumnDef( index.column() ).get("type")
        # logging.info([colVal,type1])
        if type1== "Str":
            if(colVal is None):
                return None
            else:                
                return str(colVal) #.toString();
        elif type1=="Dbl":
            if(colVal is None): 
                return None
            else:                
                return float(colVal) #.toDouble();
        elif type1=="Int":
            if(colVal is None): 
                return None
            else:                
                return int(colVal);
        elif type1=="Date":
            if(colVal is None): 
                return None
            else:                
                return colVal #.toDate();
        elif type1=="DateTime":
            if(colVal is None): 
                return None
            else:                
                return colVal #.toDateTime();
        return str(colVal)
        # try:
        #     # logging.info([whatRow,index.column()])
        #     # if(not len(self.dataIndex)==0):
        #     #     whatRow = self.dataIndex[whatRow];

        #     colVal = self._data.iloc[whatRow,index.column()];
        #     type1=self.getColumnDef( index.column() ).get("type")
        #     # logging.info([colVal,type1])
        #     if type1== "Str":
        #         if(colVal is None):
        #             return None
        #         else:                
        #             return str(colVal) #.toString();
        #     elif type1=="Dbl":
        #         if(colVal is None): 
        #             return None
        #         else:                
        #             return float(colVal) #.toDouble();
        #     elif type1=="Int":
        #         if(colVal is None): 
        #             return None
        #         else:                
        #             return int(colVal);
        #     elif type1=="Date":
        #         if(colVal is None): 
        #             return None
        #         else:                
        #             return colVal #.toDate();
        #     elif type1=="DateTime":
        #         if(colVal is None): 
        #             return None
        #         else:                
        #             return colVal #.toDateTime();
        #     return str(colVal)
        # except IndexError as e:
        #     logging.info(e)
        #     pass
        # except KeyError:
        #     logging.info(e)
        #     pass

    def setMultiselectionMobileMode(self, value):
        if(self.multiselectionMobileMode != value):
            self.multiselectionMobileMode = value;
            self.multiselectionMobileModeChanged.emit();


    def setHasMultiselection(self,value):
        if(value != self._hasMultiselection):
            self._hasMultiselection = value;
            if(not self._hasMultiselection and self.highlightRows.size()>1):
                self.setHighlightRow(-1, 0);
            self.hasMultiselectionChanged.emit()


    def isHighlightRow(self, rowNum):
        return self.highlightRows.count(rowNum)>0
        
    @Slot(result=int)
    def countHighlightRows(self):
        return self.highlightRows.size();
    @Slot(result=str)
    def getHighlightRows(self):
        toRet=[]
        for curRow in self.highlightRows._data:
            toRet.append(curRow);
        return json.dumps(toRet)
    @Slot()
    def dataNeedSort(self):
        self.columnsChangedALS = True;
    # Q_INVOKABLE void setHighlightRow(int rowNum, int modifiers);
    @Slot(int,int)
    def setHighlightRow(self, rowNum,  modifiers):
        logging.info("begin")
        pressedCtrl = modifiers & Qt.ControlModifier
        pressedShift = modifiers & Qt.ShiftModifier

        fireRowChanged=False
        minRow=-1 
        maxRow=-1

        if(self.multiselectionMobileMode):
            pressedCtrl = True;
        # rowNum=len(self.highlightRows) #.count(rowNum) #std::set<int>
        if(self.highlightRows.count(rowNum)):#error here
            # self.highlightRows.erase(rowNum);
            # if(self.highlightRows.empty()):
            #     self._highlightRow = -1;
            # else:
            #     self._highlightRow = self.highlightRows.begin();
            # fireRowChanged = True;
            # minRow = rowNum; 
            # maxRow = rowNum;
            # self.lastHighlightRow = -1;
            if(not self._hasMultiselection or pressedCtrl or self.highlightRows.size()== 1):
                #deselect it
                self.highlightRows.erase(rowNum);
                if(self.highlightRows.empty()):
                    self._highlightRow = -1;
                else:
                    self._highlightRow = self.highlightRows.begin();
                fireRowChanged = True;
                minRow = rowNum; 
                maxRow = rowNum;
                self.lastHighlightRow = -1;
            else:
                if(self._hasMultiselection):
                    minRow = self.highlightRows.begin();
                    maxRow = self.highlightRows.rbegin();
                    self.highlightRows.clear();
                    self.highlightRows.insert(rowNum);
                    self._highlightRow = rowNum;
                    self.lastHighlightRow = rowNum;
                    fireRowChanged = True;
        else:
            if(rowNum == -1):
                #select none
                if(not self.highlightRows.empty()):
                    #clear selection
                    minRow = self.highlightRows.begin();
                    maxRow = self.highlightRows.rbegin();
                    self.highlightRows.clear();
                    self._highlightRow = -1;
                    self.lastHighlightRow = -1;
                    fireRowChanged = True;
            elif(rowNum == -2):
                #select all
                for irow in range(self.rowCount()): #.size(); irow++):
                    self.highlightRows.insert(irow);
                fireRowChanged = True;
                self._highlightRow = len(self._data)-1
                self.lastHighlightRow = self._highlightRow
                if(not self.highlightRows.empty()):
                    minRow = self.highlightRows.begin()
                    maxRow = self.highlightRows.rbegin()
            else :
                if(not self._hasMultiselection or (not pressedCtrl and not pressedShift)):
                    if(not self.highlightRows.empty()):
                        minRow = self.highlightRows.begin();
                        maxRow = self.highlightRows.rbegin();
                        self.highlightRows.clear();
                if(not self._hasMultiselection or not pressedShift or self.lastHighlightRow == -1):
                    self.highlightRows.insert(rowNum);
                else:
                    fromRow = self.lastHighlightRow;
                    toRow = rowNum;
                    if(fromRow > toRow):
                        tmp=fromRow
                        fromRow= toRow
                        toRow=tmp
                    minRow = fromRow;
                    maxRow = toRow;
                    while(fromRow <= toRow):
                        self.highlightRows.insert(fromRow);
                        fromRow+=1

                self._highlightRow = rowNum;
                self.lastHighlightRow = rowNum;
                fireRowChanged = True;
                if(minRow < 0 or rowNum < minRow): 
                    minRow = rowNum;
                if(maxRow < 0 or rowNum > maxRow): 
                    maxRow = rowNum;

        if(fireRowChanged):
            self.highlightRowChanged.emit();
            if(minRow != -1):
                pass
                #qDebug() << rowNum << minRow << maxRow;
                logging.info("dataChanged")
                self.dataChanged.emit(self.index(minRow, 0), self.index(maxRow, len(self.properties) - 1), [roleHighlight]);
        logging.info("end")
    # Q_INVOKABLE void setHighlightRows(bool emptySel, const QVector<int>& rowsIdx);
    def insertManyAt(self,datas,row):
        self.beginInsertRows(QModelIndex(), row, row + len(datas) - 1)
        srow = row;
        for obj in datas:
            self._data.insert(srow,obj)
            srow+=1
        self.endInsertRows();
    def append(self,obj):
        row=len(self._data)
        self.insertAt(obj,row)
        # self.insertManyAt([obj],row)
    def appendRows(self,objs):
        row=len(self._data)
        self.insertManyAt(objs,row)        
    def insertAt(self,obj,row):
        self.beginInsertRows(QModelIndex(), row, row)
        self._data.insert(row,obj)
        self.endInsertRows();
    def removeRowsAt(self, rows):
        if len(rows)==0:
            return
        rows.sort(reverse=True)
        self.beginRemoveRows(QModelIndex(), rows[-1], rows[0])
        for row in rows:
            print(row)
            del self._data[row]
            self.highlightRows.erase(row)
        self.endRemoveRows()
    def removeAt(self, row):
        if row < 0 or row >=len(self._data):
            return
        self.beginRemoveRows(QModelIndex(), row, row)
        del self._data[row]
        self.endRemoveRows()
        self.highlightRows.erase(row)
    @Slot(bool,list)
    def setHighlightRows(self, emptySel, rowsIdx):

        fireRowChanged=False
        minRow=-1
        maxRow=-1

        if(emptySel):
            if(not len(highlightRows)==0):
                #clear selection
                minRow = self.highlightRows.begin();
                maxRow = self.highlightRows.rbegin();
                highlightRows.clear();
                highlightRow = -1;
                self.lastHighlightRow = -1;
                fireRowChanged = true;
        if(not len(rowsIdx)==0):
            for irow in range(len(rowsIdx)):
                highlightRows.insert(rowsIdx[irow]);
            fireRowChanged = True;
            highlightRow = rowsIdx[len(rowsIdx)-1];
            self.lastHighlightRow = highlightRow;
            if(not len(self.highlightRows)==0):
                if(minRow < 0 or self.highlightRows.begin() < minRow):
                    minRow = self.highlightRows.begin();
                if(maxRow < 0 or self.highlightRows.rbegin() > maxRow):
                    maxRow = self.highlightRows.rbegin();

        if(fireRowChanged):
            self.highlightRowChanged.emit()
            if(minRow != -1):
                #qDebug() << rowNum << minRow << maxRow;
                self.dataChanged.emit(QModelIndex(index(minRow, 0)), QModelIndex(index(maxRow, properties.size() - 1)), [roleHighlight]);

